B树是什么,不用说了,相信大家都懂,我今天只想详细地说一下B树的插入和删除的详细算法,为了以后自己看起来方便,并且附上自己的代码,希望对各位也能够有所帮助。
B树的插入算法:
1)用查找的方法为x找到所在的位置,查找路径终于某个空树,然后把x插入到其父结点的有序位置上,注意B树所有的插入都是在叶结点上进行。
2)如果插入x后,结点不超长,则插入完毕;否则,进入下一步,进行超长处理。
3)将超长结点一分为二,将“中间元素”递归地插入上层结点中。
4)如插入波及根,当根上溢时,把根一分为2,并将中间元素上移,而产生含单元素的新根,使B树升高。
将超长结点一分为二的操作方法:
设结点d超长,即d的元素达M个,令k=M/2向上取整,元素k作为中间元素,其左侧的元素和相应的指针仍保存在结点d中,但是需要修改d的长度,将排在k元素右侧的元素和相应的指针移入新结点e中,将k连同指向结点e的指针一起插入到d的父结点f中,排在指向结点d的指针右侧。
注意:
为了避免回溯,我的代码从上到下在查找x位置的时候,只要遇见的结点是满的,就先分解,这样,当我们插入完成后,即使需要超长处理,也只会停止在其父亲结点,不会波及更上层的结点。
我的插入代码如下:
bool CBTree::InsertNode(int nKey)
{
int i = 0;
if (m_Root == NULL)
{
/*
如果是空树,就生成一个新的节点,修改关键字个数,设置关键字,设为叶节点,然后,修改根指针。
*/
BNode * pNewNode = new BNode;
if (!pNewNode)
return false;
pNewNode->m_Key[0] = 1;
pNewNode->m_Key[1] = nKey;
pNewNode->m_bLeaf = true;
m_Root = pNewNode;
return true;
}
//非空,查找叶节点
BNode * pCurNode = m_Root;
BNode * pNewNode = NULL;
BNode * pParent = NULL;
int nParentIndex = 0;
BNode * pChild = NULL;
int nChildIndex = 0;
int nMidKey = 0;
int k = 0;
/*
寻找到叶节点,因为B树是在叶节点上插入,在从根节点到叶节点的路径上,如果遇见满节点,就对其进行分裂,这样的好处,就是如果
叶节点分裂,不需要回溯,直接在父亲节点上插入即可,因为,在到达叶节点之前,已经对所有的满节点进行了分裂。
*/
while (pCurNode->m_bLeaf == false)
{
/*
遍历当前节点的关键字,如果有相等的,就直接返回,因为已经存在了,否则,找到子树,并保存在pChild和nChildIndex。
*/
for (i = 1; i <= pCurNode->m_Key[0]; i++)
{
if (nKey == pCurNode->m_Key[i])
{
//已经存在,插入返回
return true;
}
if (nKey < pCurNode->m_Key[1])
{
pChild = pCurNode->m_ChildRoot[0];
nChildIndex = 0;
break;
}
if (nKey < pCurNode->m_Key[i])
{
pChild = pCurNode->m_ChildRoot[i-1];
nChildIndex = i - 1;
break;
}
}
if (i > pCurNode->m_Key[0])
{
pChild = pCurNode->m_ChildRoot[i - 1];
nChildIndex = pCurNode->m_Key[0];
}
//判断节点是否满了,如果满了则分裂。
if (pCurNode->m_Key[0] == B_TREE_ORDER - 1)
{
pNewNode = new BNode;
if (!pNewNode)
return false;
pNewNode->m_bLeaf = false;
nMidKey = (B_TREE_ORDER + 1) / 2; //向上取整加1
//拷贝关键字
for (i = nMidKey + 1; i <= pCurNode->m_Key[0]; i++)
{
pNewNode->m_Key[pNewNode->m_Key[0] + 1] = pCurNode->m_Key[i];
pNewNode->m_Key[0]++;
}
//拷贝孩子
for (i = nMidKey ; i <&